/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package me.zhengjie.modules.enrstu.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.enrstu.domain.AdministrativeDivision;
import me.zhengjie.modules.enrstu.repository.AdministrativeDivisionRepository;
import me.zhengjie.modules.enrstu.service.ManageService;
import me.zhengjie.modules.enrstu.service.dto.ManageCreateOrUpdateDto;
import me.zhengjie.modules.enrstu.service.dto.ManageDto;
import me.zhengjie.modules.enrstu.service.dto.ManageQueryCriteria;
import me.zhengjie.modules.enrstu.service.enums.ManageStatusType;
import me.zhengjie.modules.enrstu.domain.Manage;
import me.zhengjie.modules.enrstu.repository.ManageRepository;

import me.zhengjie.modules.enrstu.service.mapstruct.ManageMapper;
import me.zhengjie.modules.system.domain.Dept;
import me.zhengjie.modules.system.domain.User;
import me.zhengjie.modules.system.repository.DeptRepository;
import me.zhengjie.modules.system.repository.UserRepository;
import me.zhengjie.modules.system.service.DictDetailService;
import me.zhengjie.modules.system.service.dto.DictDetailDto;
import me.zhengjie.utils.*;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.time.Year;
import java.util.*;
import java.util.stream.Collectors;




/**
 * @website https://eladmin.vip
 * @description 服务实现
 * @author yyx
 * @date 2024-10-14
 **/
@Service
@RequiredArgsConstructor
@Slf4j
public class ManageServiceImpl implements ManageService {

    private final ManageRepository manageRepository;
    private final DeptRepository deptRepository;
    private final AdministrativeDivisionRepository adRepository;
    private final ManageMapper manageMapper;
    private final UserRepository userRepository;
    private  final DictDetailService dictDetail;

    @Override
    public PageResult<ManageDto> queryAll(ManageQueryCriteria criteria, Pageable pageable){
        

        //条件查询是否按照启动状态查询

        List<ManageDto> manageList = manageRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {
            Predicate predicate = QueryHelp.getPredicate(root, criteria, criteriaBuilder);
            //TODO: 待优化->pmfProjectStartStatus -->编码 0，1，2

            // 设置排序规则
            criteriaQuery.orderBy(
                    criteriaBuilder.asc(
                            criteriaBuilder.selectCase()
                                    .when(criteriaBuilder.equal(root.get("pmfProjectStartStatus"), 1), 1)
                                    .when(criteriaBuilder.equal(root.get("pmfProjectStartStatus"), 0), 2)
                                    .when(criteriaBuilder.equal(root.get("pmfProjectStartStatus"), 2), 3)
                                    .otherwise(4)
                    ),
                    // 对于 "待启用" 状态按修改时间倒序排序
                    criteriaBuilder.desc(
                            criteriaBuilder.selectCase()
                                    .when(criteriaBuilder.equal(root.get("pmfProjectStartStatus"), 0), root.get("gmtModified"))
                    ),
                    // 对于 "已过期" 状态按公布时间倒序排序
                    criteriaBuilder.desc(
                            criteriaBuilder.selectCase()
                                    .when(criteriaBuilder.equal(root.get("pmfProjectStartStatus"), 2), root.get("pmfPublishTime"))
                    )
            );
            return predicate;
        },pageable).getContent().stream().filter(this::mgQyHandleFilter).map(this::mgQyHandleMap).collect(Collectors.toList());

        return PageUtil.toPage(manageList,manageList.size());
    }

    @Override
    public List<ManageDto> queryAll(){
        return manageRepository.findAll().stream().filter(this::mgQyHandleFilter)
                .map(this::mgQyHandleMap).collect(Collectors.toList());
    }

    @Override
    public ManageDto findById(String id) {
        Manage manage = manageRepository.findById(id).orElseGet(Manage::new);
        if (!manage.getPmfCreatePersonId().equals(SecurityUtils.getCurrentUserId())){
            manage.setId(null);
        }
        ValidationUtil.isNull(manage.getId(),"Manage","id",id);
        return this.mgQyHandleMap(manage);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void createOrUpdateMg(ManageCreateOrUpdateDto resources){
        //校验数据正确性
        this.mgFeignCheck(resources);
        Manage manage = new Manage();
        BeanUtils.copyProperties(resources,manage);
        //判断是否上送id，上送-->更新，不上送-->修改
        Manage sava = ObjectUtil.isNull(resources.getId()) ? this.create(manage) : this.update(manage);
        // 强制刷新数据库会话
        manageRepository.flush();
        //判断是否为启用
        if (resources.getIsStatusManage()) {
            this.startManage(sava.getId());
        }
    }



    //前台 报名-提醒区 ：

    @Override
    public void deleteAll(String id) {
        ManageDto manageDto = this.findById(id);
        //判断项目是否为已启动
        if (ManageStatusType.STARTED.getDescription().equals(manageDto.getPmfProjectStartStatus())){
            throw new BadRequestException("项目已启用，不允许删除数据！");
        }
        manageRepository.deleteById(id);
    }



    @Override
    @Transactional
    public void revocationManage(String id) {
        //1.根据id查询数据库
        Manage manage = manageMapper.toEntity(this.findById(id));
        //2.项目进行撤销并且判断是否符合撤销条件
        manage.setPmfProjectStartStatus(this.revOrStartHandle(manage,  false));

        manageRepository.save(manage);
    }

    @Override
    @Transactional
    public void startManage(String id) {
        //1.根据id查询数据库
        Manage manage = manageMapper.toEntity(this.findById(id));
        //2.判断当前项目时间是否符合启用条件：01 项目已启用，当前项目正在进行中，无法启用 02 项目已过期无法启用
        Integer status =  this.revOrStartHandle(manage, true);
        manage.setPmfProjectStartStatus(status == 1 ? ManageStatusType.STARTED.getDescription() : ManageStatusType.TO_BE_STARTED.getDescription());
        Manage manageUpdate = new Manage();
        manageUpdate.copy(manage);
        manageRepository.save(manageUpdate);
    }



    @Override
    public  List<Integer> getAfflictionRange(boolean isListAffRange){
        List<DictDetailDto> dictS = dictDetail.getDictByName("manage_Affliction_range");
        int  YEARS_AGO_FIVE = 0;
        int  YEARS_AGO_THREE = 0;

        for (DictDetailDto dict : dictS){
            if(dict.getLabel().equals("YEARS_AGO_FIVE")){
                YEARS_AGO_FIVE = Integer.parseInt(dict.getValue());
            }
            if(dict.getLabel().equals("YEARS_AGO_THREE")){
                YEARS_AGO_THREE = Integer.parseInt(dict.getValue());
            }
        }
        List<Integer> years = new ArrayList<>();
        int year = Calendar.getInstance().get(Calendar.YEAR);
        if (isListAffRange){
            //返回查询列表页面所属年度的下拉返回
            for (int i = year - YEARS_AGO_FIVE; i <= year + YEARS_AGO_THREE ; i++) {
                years.add(i);
            }
        }else {
            //返回新增页面所属年度的下拉返回
            for (int i = year; i <= year + YEARS_AGO_THREE ; i++) {
                years.add(i);
            }
        }


        return years;
    }

    @Override
    public ManageDto findUserStartMg() {
        //获取当前用户返回当前用户所在城市
        User user = userRepository.findById(SecurityUtils.getCurrentUserId()).orElseGet(User::new);
        //获取当前用户的城市
        AdministrativeDivision ad = adRepository.findById(user.getCity().getId()).orElseGet(AdministrativeDivision::new);
        //获取顶级省/市
        AdministrativeDivision pads = adRepository.findBySignAndLevel(ad.getAdministrativeDivisionSign(),10);
        String name =  deptRepository.findByNameLike(pads.getAdministrativeDivisionName()).getName();
        ManageDto manageDto = manageRepository.findAll().stream().map(this::mgQyHandleMap).filter(m -> {
            User u = userRepository.findById(m.getPmfCreatePersonId()).orElseGet(User::new);
            return u.getDept().getName().equals(name) && m.getPmfProjectStartStatus().equals(1);
        }).findFirst().orElseGet(ManageDto::new);
        log.info("findStartMg:{}",manageDto);
        return manageDto;
    }

    @Override
    public ManageDto findSysUserStartMg() {
        //获取当前系统用户对应的项目
        return this.queryAll().stream().filter(m -> m.getPmfProjectStartStatus().equals(1)).findFirst().orElseGet(ManageDto::new);    }

    @Transactional(rollbackFor = Exception.class)
    public Manage create(Manage manage) {
        //获取当前用户id
        manage.setPmfCreatePersonId(SecurityUtils.getCurrentUserId());
        manage.setId(IdUtil.simpleUUID());
        //设置为待启用
        manage.setPmfProjectStartStatus(this.statusCompute(manage));
        manage.setPmfCreatePersonId(SecurityUtils.getCurrentUserId());

        return manageRepository.save(manage);
    }

    /**
     * 修改
     * @param manage /
     * @return /
     */
    @Transactional(rollbackFor = Exception.class)
    public Manage update(Manage manage) {
        Manage update = manageMapper.toEntity(this.findById(manage.getId()));
        ValidationUtil.isNull( manage.getId(),"Manage","id",update.getId());
        manage.copy(update);
        return manageRepository.save(manage);
    }

    /**
     *  创建或修改项目前对于上送实体类字段的校验
     * @param resources /
     */
    private void mgFeignCheck(ManageCreateOrUpdateDto resources){

        //项目名称的校验
        if (ObjectUtil.isNotNull(resources.getPmfName())){
            //校验：项目名称是否已存在
            if(manageRepository.count((Root<Manage> root, CriteriaQuery<?> query, CriteriaBuilder cb)->{
                Predicate pmfName = cb.equal(root.get("pmfName"), resources.getPmfName());
                return cb.and(pmfName);
            }) > 0){
                throw new BadRequestException("项目名称已存在！");
            }
            //校验：项目名称是否大于50个字符
            if (resources.getPmfName().length() >= 50){
                throw new BadRequestException("项目名称不能大于50字符！");
            }
        }
        //所属年度的校验
        if(ObjectUtil.isNotNull(resources.getPmfAffiliationAnnual())){
            //校验：所属年度不能早于当前年份
            if(!(resources.getPmfAffiliationAnnual() >= Year.now().getValue())){
                throw new BadRequestException("所属年度不能早于当前年份");
            }
        }
        //家长填报时间段校验
        Date pmfParentFillStartTime = resources.getPmfParentFillStartTime();
        Date pmfParentFillEndTime = resources.getPmfParentFillEndTime();
        if(ObjectUtil.isNotNull(pmfParentFillStartTime) || ObjectUtil.isNotNull(pmfParentFillEndTime)){
            this.mgTimeCheck(pmfParentFillStartTime,pmfParentFillEndTime,"家长填报");
        }
        //学校初审时间段校验
        Date pmfSchoolCheckStartTime = resources.getPmfSchoolCheckStartTime();
        Date pmfSchoolCheckEndTime = resources.getPmfSchoolCheckEndTime();
        if(ObjectUtil.isNotNull(pmfSchoolCheckStartTime) || ObjectUtil.isNotNull(pmfSchoolCheckEndTime)){
            this.mgTimeCheck(pmfSchoolCheckStartTime,pmfSchoolCheckEndTime,"学校初审");
        }
        //教育局复审时间段校验
        Date pmfEducationCheckStartTime = resources.getPmfEducationCheckStartTime();
        Date pmfEducationCheckEndTime = resources.getPmfEducationCheckEndTime();
        if(ObjectUtil.isNotNull(pmfEducationCheckStartTime) || ObjectUtil.isNotNull(pmfEducationCheckEndTime)){
            this.mgTimeCheck(pmfEducationCheckStartTime,pmfEducationCheckEndTime,"教育局复审");
        }

        //最小年龄限制
        if(ObjectUtil.isNotNull(resources.getPmfMinimumAge())){
            if (resources.getPmfMinimumAge().length() >= 2){
                throw new BadRequestException("最小年龄限制不超过2字符！");
            }
        }

        //更新项目校验
        if(ObjectUtil.isNotNull(resources.getId())){
            //id校验
            ManageDto manageDto = this.findById(resources.getId());
            //项目状态校验
            if(ManageStatusType.STARTED.getDescription().equals(manageDto.getPmfProjectStartStatus())){
                throw new BadRequestException("项目已启用，不允许修改数据");
            }
            if(ManageStatusType.EXPIRED.getDescription().equals(manageDto.getPmfProjectStartStatus())){
                throw new BadRequestException("项目已过期，不允许修改数据！");
            }


        }
    }
    /**
     * 项目管理时间段校验
     */
    private void mgTimeCheck(Date mgStart,Date mgEnd,String label){
        //更新时，会有与数据库原日期进行比较


            //起始日期大于当前始日期，结束日期大于当前日期，起始日期大于结束日期
            if (ObjectUtil.isNotNull(mgStart) || ObjectUtil.isNotNull(mgEnd)){
                if (DateUtils.compare(mgStart) < 0 || DateUtils.compare(mgEnd) < 0){
                    throw new BadRequestException(label+"起始或结束时间不能小于当前日期，请重新设置！,");
                }
            }
            if (ObjectUtil.isNotNull(mgStart) && ObjectUtil.isNotNull(mgEnd)){
                if (DateUtils.compare(mgStart,mgEnd) > 0){
                    throw new BadRequestException(label+"起始时间不能大于结束时间，请重新设置！");
                }
            }

    }

    /**
     * 自动计算项目状态:待启用、已启用
     */
    private  Integer statusCompute(Manage manage) {
        return DateUtils.compare(manage.getPmfPublishTime()) > 0 ?  ManageStatusType.TO_BE_STARTED.getDescription() : ManageStatusType.EXPIRED.getDescription();
    }

    /**
     *撤销项目管理&启动项目管理处理
     */
    private Integer revOrStartHandle(Manage manage,Boolean manualStart){
        //判断项目是否是否已过期
        if (DateUtils.compare(manage.getPmfPublishTime()) <= 0){
            throw new BadRequestException("当前项目已过期，无法进行启用或者撤回");
        }
        //判断是否为启动项目
        if (manualStart){
            //判断实体类是否每个必填项属性是否填写
            Map<String, Object> beanMap = BeanUtil.beanToMap(manage, false, false);
            beanMap.forEach((key, value)->{
                if (ObjectUtil.equal(key, "pmfMinimumAge") ||ObjectUtil.equal(key, "pmfProjectStartStatus") || ObjectUtil.equal(key, "gmtCreate") || ObjectUtil.equal(key, "gmtModified") || ObjectUtil.equal(key, "id")){
                    return;
                }
                if (ObjectUtil.isNull(value)){
                    throw  new BadRequestException("存在必填项未填写，请补充完整后启用");
                }
            });
            //这里方法拿到的数据就是当前用户所在区的数据：例如：当前登录用户是沙坪坝的管理员或者沙坪坝审核员、家长、学校等，那么查出来的项目就是沙坪坝区的项目管理数据 但是只有沙坪坝区的管理员才能进行增删改查，其他只有查询权限
            this.queryAll().forEach(mg->{
                if (ObjectUtil.isNotNull(mg.getPmfProjectStartStatus())){
                    if(mg.getPmfProjectStartStatus()==1){
                        throw new BadRequestException("当前存在正在进行的项目，无法启用");
                    }
                }
            });
            return ManageStatusType.STARTED.getDescription();
        }else {
            if (ObjectUtil.equal(manage.getPmfProjectStartStatus(),ManageStatusType.TO_BE_STARTED.getDescription())){
                throw new BadRequestException("项目状态为待启用，无法撤销");
            }
            if (DateUtils.compare(manage.getPmfParentFillStartTime()) <= 0){
                throw new BadRequestException("家长填报时间已开始，无法撤回");
            }
            return ManageStatusType.TO_BE_STARTED.getDescription();
        }

    }

    /**
     * 用于manage 查询、分页查询的条件统一处理的方法
     * @param manage /
     * @return /
     */
    private ManageDto mgQyHandleMap(Manage manage) {
        //判断是否已过期,只有已启用的项目才需要判断项目是否过期
        if (ObjectUtil.equal(manage.getPmfProjectStartStatus(), ManageStatusType.STARTED.getDescription())){
            Integer newStatus = this.statusCompute(manage);
            if (ObjectUtil.equal(newStatus, ManageStatusType.EXPIRED.getDescription())){
                manage.setPmfProjectStartStatus(newStatus);
                manageRepository.save(manage);
            }
        }

        ManageDto manageDto = manageMapper.toDto(manage);
        manageDto.setPmfAffiliationAnnual(manage.getPmfAffiliationAnnual());
        //根据创建人id获取创建人姓名
        if (ObjectUtil.isNotNull(manage.getPmfCreatePersonId())){
            manageDto.setPmfCreatePerson(userRepository.findById(manage.getPmfCreatePersonId()).orElseGet(User::new).getUsername());
        }

        if (ObjectUtil.isNull(manageDto.getPmfMinimumAge())){
            manageDto.setPmfMinimumAge("");
        }
        return manageDto;
    }

    private boolean mgQyHandleFilter(Manage manage){
        User cuUser = userRepository.findById(SecurityUtils.getCurrentUserId()).orElseGet(User::new);
        if (cuUser.getId().equals(manage.getPmfCreatePersonId())){
            return true;
        }
        //根据当前用户id和创建项目人id获取用户信息
        User creatUser = userRepository.findById(manage.getPmfCreatePersonId()).orElseGet(User::new);
        //初始化循环条件
        Long tempPid = cuUser.getDept().getPid();
        //构建循环体
        for(;;){
            //根据将pid作为id进行查询部门
            Dept dept = deptRepository.findById(tempPid).orElseGet(Dept::new);
            //中止条件
            if(dept.getId().equals(creatUser.getDept().getId())){
                return true;
            }
            //停止条件
            if (dept.getPid() == null){
                return false;
            }
            tempPid = dept.getPid();
        }
    }
}